package apibasej.db.metadata;
/**
 * http://code.google.com/p/apibasej/
 * 
 * @author Julio Azevedo Lessa - apibasej@gmail.com
 * 
 */
public class Col<T> {

	private Tab tab;
	private String name;
	private Integer displaySize;
	private Integer precision;
	private Integer scale;
	private Integer type; //java.sql.Types
	private String typeName;
	private Boolean notNull;
	private Boolean signed;
	
	private Boolean isPk;
	
	// SE esta coluna for FK a tab e col que ela referencia devem ser setadas
	private Col<T> foreignCol; // não precisa da tabela, pois a col já tem a tab
	
	private Class<T> classCol;
	
	private boolean isIndirectForeingCol = false;
	
	//private Integer ordem;// posição de leitura
	
	
	public Col(Tab tab, String name, Class<T> classCol){// para se usado no UtilDB recebendo somente strings e tipos
		this(tab, name, classCol, null, null, null, null, null, null, null, null, null, false);
	}
	
	public Col(Tab tab, String name, Class<T> classCol, Integer displaySize, Integer precision, Integer scale, Integer type, String typeName, Boolean notNull, Boolean signed, Boolean isPk, Col<T> foreignCol, boolean isIndirectForeingCol) {
		
		// para deixar mais claro erros nas instanciações das classes de constantes, ver gerador
		if(tab==null) throw new NullPointerException("Tab can not be null!");
		//if(tab.getTabGroup()==null) throw new NullPointerException("tab.getTabGroup() can not be null!");
		
		//System.out.println("tab>"+tab);
		//System.out.println(">>>>>>"+tab+" - "+tab.getCols().size()+" - "+PARTE_PROCESSO.getInst()+" - "+PARTE_PROCESSO.getInst().getCols().size());
		tab.getCols().put(name,this);// ivitar criar Cols em Tabs existentes pois pode adicionar uma coluna desnecessária na tab ou substituir uma existente
		this.tab = tab;
		this.name = name;
		this.classCol = classCol;
		this.displaySize = displaySize;
		this.precision = precision;
		this.scale = scale;
		this.type = type;
		this.typeName = typeName;
		this.notNull = notNull;
		this.signed = signed;
		this.isPk = isPk;
		this.foreignCol = foreignCol;
		this.isIndirectForeingCol = isIndirectForeingCol;
	}
	

	public boolean isIndirectForeingCol() {
		return isIndirectForeingCol;
	}
	
	
	//@SuppressWarnings("unchecked")
	//public T cast(Object o){
	//	return (T)o;
	//}
	
	public Class<T> getClassCol(){
		return classCol;
	}
	
	public <C> Col<C> getColLinkExt(Col<C> colExt, boolean findIndirectLinks){
		
		//if(this.isIndirectForeingCol() || colExt.isIndirectForeingCol()){
		//	System.out.println(">>"+this+"-"+colExt+" | "+this.isIndirectForeingCol()+"-"+colExt.isIndirectForeingCol());
		//}
		
		// se a FK da currentCol aponta diretamente para a PK colExt, e se não for uma referência indireta (como id_ent_externa)
		//retorna a col externa
		if(colExt.isPk() && colExt.equals(this.getForeignCol()) && !this.isIndirectForeingCol())  {
			return colExt;
		}
		
		Col<C> fkColExt = colExt.getForeignCol();
		
		// se a FK da colExt aponta para a currentCol PK.   , e se não for uma referência indireta (como id_ent_externa)
		// EX: currentCol=org_pessoa_fisica.id & fkFather=org_cliente.id_pessoa_fisica
		// retorna a col externa
		if(this.isPk() && this.equals(fkColExt) && !colExt.isIndirectForeingCol()){
			return colExt;
		}
		
		if(findIndirectLinks){
			// se thisCol aponta para uma tabela intermediária que a pk da colExterna também aponta 
			//(como no caso this=org_endereco.id_ent_externa>sis_id_ctrl.id<org_cliente.id)
			//neste caso retorna a col externa, EX: org_cliente.id
			if(colExt.isPk() && !this.isPk() && fkColExt!=null && fkColExt.equals(this.getForeignCol())){
				return colExt;
			}
			
			// o inverso da situação acima:
			// se colExterna aponta para uma tabela intermediária que a pk da thisCol também aponta 
			//(como no caso this=org_cliente.id>sis_id_ctrl.id<org_endereco.id_ent_externa)
			//neste caso retorna a col externa, EX: org_endereco.id_ent_externa
			if(this.isPk() && !colExt.isPk() && getForeignCol()!=null && this.getForeignCol().equals(fkColExt)){
				return colExt;
			}
		}
		
		//se a colEx não tem vínculo com a col atual retorna null
		return null;
		
		/*
		Col<?> fkColExt = colExt.getForeignCol();
		//System.out.println("ZZZZZZZ>>>"+colExt+" - "+currentCol+" | "+colExt.getForeignCol()+" - "+currentCol.getForeignCol()+" # "+rowFather.get(ct));
		return (colExt.isPk() && colExt.equals(this.getForeignCol()))  // se a FK da currentCol aponta para a PK colExt
			|| (colExt.isPk() && !this.isPk() && fkColExt!=null && fkColExt.equals(this.getForeignCol())) // se aponta para uma tabela intermadiária que a pk da tab pai também aponta (como no caso org_endereco.id_ent_externa>sis_id_ctrl.id)
			|| (this.isPk() && this.equals(fkColExt)); // se a FK da colExt aponta para a currentCol PK. EX: currentCol=org_pessoa_fisica.id & fkFather=org_cliente.id_pessoa_fisica
			*/
	}
	
	// para gerador ...
	public String getStringFieldForGenerator(String packageRoot){//String nameClassTabGroup
		String paramForeignCol = "null";
		if(getForeignCol()!=null){
			Tab fTab = getForeignCol().getTab();
			if(!fTab.getClass().equals(Tab.class)){// se a classe não for Tab significa que esta tabela foi adicionada manualmente no LoaderModelDB ...
				paramForeignCol = fTab.getClass().getName()+"."+getForeignCol().getName();
			}else{
				paramForeignCol = packageRoot+"."+fTab.getName()+"."+getForeignCol().getName(); //nameClassTabGroup+"."+; não precisa do tabGroup porque já está no mesmo pacote
			}
			//System.out.println(">>>"+fTab.getClass().getName()+" - "+getForeignCol().getClass().getName());
			
		}
		String cn = getClassCol().getName();
		
		if(getClassCol().isPrimitive() || getClassCol().isArray()) {
			cn = getClassCol().getSimpleName();
		}
		
		return "\r\n\tpublic static final Col<"+cn+"> "+getName()+" = new Col<"+cn+">(getInstance(),\""+getName()+"\","+cn+".class,"+getDisplaySize()+","+getPrecision()+","+getScale()+","+getType()+",\""+getTypeName()+"\","+isNotNull()+","+isSigned()+","+isPk()+","+paramForeignCol+","+isIndirectForeingCol()+");";
	}

	//@Override
	//public int hashCode() {
	//	return getName().hashCode();
	//}
	
	@Override
	public boolean equals(Object obj) {
		return this==obj;
		/*
		if( !(obj instanceof Col<?>) ) return false;
		Col<?> c = (Col<?>)obj;
		boolean ok = this.getName().equals(c.getName());
		if(ok && c.getTab()!=null && this.getTab()!=null) ok = this.getTab().equals(c.getTab());
		return ok;
		*/
	}
	
	//@Override
	//public String toString() {
	//	return getName();
	//}
	
	
	public void setIndirectForeingCol(boolean isIndirectForeingCol) {
		this.isIndirectForeingCol = isIndirectForeingCol;
	}
	
	
	public String getName() {
		return name;
	}
	public String getNameWithTab() {
		return getTab().getName()+"."+name;
	}
	
	public Col<T> getForeignCol() {
		return foreignCol;
	}
	public Tab getTab() {
		return tab;
	}
	public Integer getDisplaySize() {
		return displaySize;
	}
	public Integer getPrecision() {
		return precision;
	}
	public Integer getScale() {
		return scale;
	}
	public Integer getType() {
		return type;
	}
	public String getTypeName() {
		return typeName;
	}
	public boolean isNotNull() {
		if(notNull==null) return false; // para não dar NullPointerExeception
		return notNull;
	}
	public boolean isSigned() {
		if(signed==null) return false; // para não dar NullPointerExeception
		return signed;
	}
	public boolean isPk() {
		if(isPk==null) return false; // para não dar NullPointerExeception
		return isPk;
	}


	public void setPk(Boolean isPk) {
		this.isPk = isPk;
	}
	public void setTab(Tab tab) {
		this.tab = tab;
	}
	public void setForeignCol(Col<T> foreignCol) {
		this.foreignCol = foreignCol;
	}
	public void setName(String name) {
		this.name = name;
	}
	public void setDisplaySize(Integer displaySize) {
		this.displaySize = displaySize;
	}
	public void setPrecision(Integer precision) {
		this.precision = precision;
	}
	public void setScale(Integer scale) {
		this.scale = scale;
	}
	public void setType(Integer type) {
		this.type = type;
	}
	public void setTypeName(String typeName) {
		this.typeName = typeName;
	}
	public void setNotNull(Boolean notNull) {
		this.notNull = notNull;
	}
	public void setSigned(Boolean signed) {
		this.signed = signed;
	}
	public void setClassCol(Class<T> classCol) {
		this.classCol = classCol;
	}
	
	
	@Override
	protected final Object clone() throws CloneNotSupportedException {
		throw new CloneNotSupportedException();
	}
	
	@Override
	public String toString() {
		return "["+getNameWithTab()+"/"+super.toString()+"]";
	}
}


